#include "BaiduPinyinApi.h"
#include "networkmanager.h"
#include "encryptutil.h"
#include "configmanager.h"

#include <QNetworkRequest>
#include <QNetworkReply>
#include <QCryptographicHash>
#include <QRandomGenerator>
#include <QFile>
#include <QTimer>
#include <QDebug>
#include <QJsonArray> 
#include <QJsonParseError>
#include <QJsonObject>

BaiduPinyinApi* BaiduPinyinApi::m_instance = nullptr;
QMutex BaiduPinyinApi::m_instanceMutex;

BaiduPinyinApi::BaiduPinyinApi(QObject *parent) : QObject(parent)
{
	// init netMgr
	m_networkMgr = NetworkManager::getInstance(this);
	m_networkMgr->setTimeout(5000);
	// load loca cache
	loadLocalCache();

	// config get key
	ConfigManager* config = ConfigManager::getInstance();
	QString encryptedAppId = config->getBaiduApiAppId();
    QString encryptedSecretKey = config->getBaiduApiSecretKey();

	// 解密key AES-128-CBC
	QByteArray key = EncryptUtil::generateKey();
	QByteArray encryptKey = EncryptUtil::generateKey();  // 复用EncryptUtil的AES密钥
    m_appId = EncryptUtil::decryptString(QByteArray::fromHex(encryptedAppId.toUtf8()), encryptKey);
    m_secretKey = EncryptUtil::decryptString(QByteArray::fromHex(encryptedSecretKey.toUtf8()), encryptKey);

	// listen config changes
	connect(config, &ConfigManager::configChanged, this, [this](ConfigGroup group, const QString& key, const QVariant& value) {
		if (group != InputMethodGroup) return;
		QByteArray encryptKey = EncryptUtil::generateKey();
        if (key == "BaiduApi_AppId") {
            m_appId = EncryptUtil::decryptString(QByteArray::fromHex(value.toString().toUtf8()), encryptKey);
        } else if (key == "BaiduApi_SecretKey") {
            m_secretKey = EncryptUtil::decryptString(QByteArray::fromHex(value.toString().toUtf8()), encryptKey);
        }
        clearPinyinCache();  // 密钥变更后清空缓存
	});
}

BaiduPinyinApi *BaiduPinyinApi::getInstance(QObject *parent)
{
	// 双重检查锁定
	if (!m_instance) {
        QMutexLocker locker(&m_instanceMutex);
        if (!m_instance) {
            m_instance = new BaiduPinyinApi(parent);
        }
    }
    return m_instance;
}

BaiduPinyinApi::~BaiduPinyinApi()
{
	// 退出前保存本地缓存
    saveLocalCache();
	qInfo() << "[BaiduPinyinApi] 本地缓存已保存：" << m_localCachePath;
}

void BaiduPinyinApi::convertToPinyin(const QString &text)
{
	// 请求数++
	m_totalRequest++;
	QString cachedPinyin = getPinyinFromCache(text);  	// 优先从缓存中去获取
	if (!cachedPinyin.isEmpty()) {
		m_cacheHit++;
		emit conversionSuccess(text, cachedPinyin);
		return;
	}
	// check Api config
	if (m_appId.isEmpty() || m_secretKey.isEmpty()) {
        emit conversionFailed(ApiErrorCode::AppIdError, "百度API密钥未配置");
        QString offlinePinyin = m_localCacheDoc.object()["polyphonic_words"].toObject().value(text).toString();
        if (!offlinePinyin.isEmpty()) {
            emit conversionSuccess(text, offlinePinyin);
        }
        return;
    }

	// long text split
	QStringList textParts;
	if (!splitLongText(text, textParts)) {
		emit conversionFailed(ApiErrorCode::LengthExceed, "输入文本长度超过限制，无法拆分");
		return;
	}

	// 开始异步处理 每段文本单独请求，最后合并结果
	auto processPart = [this](const QString& part) {
		QString salt = QString::number(QRandomGenerator::global()->generate64());
		QString sign = generateSign(m_appId, part, salt, m_secretKey);
		// build request 构建API参数（百度翻译API：zh→wyw（文言文，返回拼音））
        QMap<QString, QString> params;
        params["q"] = part;
        params["from"] = "zh";
        params["to"] = "wyw";
        params["appid"] = m_appId;
        params["salt"] = salt;
        params["sign"] = sign;
		
		// post request
		QNetworkReply* reply = m_networkMgr->sendPostRequest(m_apiUrl, params);
		connect(reply, &QNetworkReply::finished, this, [this, part, reply]() {
			if (reply->error() != QNetworkReply::NoError) {
                emit conversionFailed(ApiErrorCode::NetworkError, reply->errorString());
                // 离线兜底
                QString offlinePinyin = m_localCacheDoc.object()["polyphonic_words"].toObject().value(part).toString();
                if (!offlinePinyin.isEmpty()) {
                    emit conversionSuccess(part, offlinePinyin);
                }
                reply->deleteLater();
                return;
            }

			// Parse API response
			QByteArray response = reply->readAll();
			ApiErrorCode code = ApiErrorCode::Success;
			QString pinyin = parseApiResponse(response, code);
			if (code == ApiErrorCode::Success) {
				// 保存到缓存
				savePinyinToCache(part, pinyin);
                emit conversionSuccess(part, pinyin);
			} else {
				emit conversionFailed(code, "API响应错误");
                // 离线兜底
                QString offlinePinyin = m_localCacheDoc.object()["polyphonic_words"].toObject().value(part).toString();
                if (!offlinePinyin.isEmpty()) {
                    emit conversionSuccess(part, offlinePinyin);
                }
			}
			reply->deleteLater();
		});
	};
	// 处理所有拆分的片段
	for (const QString& part : textParts)	processPart(part);
}

void BaiduPinyinApi::testApiConfig(const QString &appId, const QString &secretKey)
{
	// 保存原有配置，测试后恢复
    QString oldAppId = m_appId;
    QString oldSecretKey = m_secretKey;
    m_appId = appId;
    m_secretKey = secretKey;

    // 测试文本：“重庆”（多音字，验证API准确性）
    convertToPinyin("重庆");

    // 监听测试结果
    connect(this, &BaiduPinyinApi::conversionSuccess, this, [this, oldAppId, oldSecretKey](const QString& text, const QString& pinyin) {
        if (text == "重庆" && pinyin.contains("chóng qìng")) {
            emit testResult(true, "API配置有效");
        } else {
            emit testResult(false, "结果异常：预期chóng qìng，实际" + pinyin);
        }
        // 恢复配置并断开信号
        m_appId = oldAppId;
        m_secretKey = oldSecretKey;
        disconnect(this, nullptr, this, nullptr);
    });

    connect(this, &BaiduPinyinApi::conversionFailed, this, [this, oldAppId, oldSecretKey](ApiErrorCode code, const QString& msg) {
        m_appId = oldAppId;
        m_secretKey = oldSecretKey;
        disconnect(this, nullptr, this, nullptr);
        emit testResult(false, QString("配置无效：错误码%1，%2").arg(static_cast<int>(code)).arg(msg));
    });
}

void BaiduPinyinApi::clearPinyinCache()
{
	QMutexLocker locker(&m_instanceMutex);
    m_memoryCache.clear();
    // 清空本地缓存的cache字段
    QJsonObject root = m_localCacheDoc.object();
    root["cache"] = QJsonObject();
    m_localCacheDoc.setObject(root);
    saveLocalCache();
    qInfo() << "[BaiduPinyinApi] 双层缓存已清空";
}

int BaiduPinyinApi::getCacheHitRate() const
{
	if (m_totalRequest == 0) return 0;
    return (m_cacheHit * 100) / m_totalRequest;		// 计算并返回缓存命中率（百分比）
}

void BaiduPinyinApi::updateApiconfig(const QString &appId, const QString &secretKey) {
	QMutexLocker locker(&m_instanceMutex);
    m_appId = appId;
    m_secretKey = secretKey;
	// key + config save
	QByteArray encryptKey = EncryptUtil::generateKey();
    QString encryptedAppId = EncryptUtil::encryptString(appId, encryptKey).toHex();
    QString encryptedSecretKey = EncryptUtil::encryptString(secretKey, encryptKey).toHex();
    // 保存到ConfigManager（最终写入config.ini）
    ConfigManager::getInstance()->setBaiduApiConfig(encryptedAppId, encryptedSecretKey);
    qInfo() << "[BaiduPinyinApi] API密钥已加密存储";

    // 清空缓存（避免旧密钥缓存干扰）
    clearPinyinCache();
}

QString BaiduPinyinApi::generateSign(const QString &appId, const QString &text, const QString &salt, const QString &secretKey)
{
	// 签名规则：appid + q + salt + secretKey → MD5哈希
    QString signStr = appId + text + salt + secretKey;
    QByteArray signBytes = QCryptographicHash::hash(signStr.toUtf8(), QCryptographicHash::Md5);
    return signBytes.toHex();
}

bool BaiduPinyinApi::splitLongText(const QString &text, QStringList &parts)
{
	if (text.length() <= m_maxTextLength) {
		parts.append(text);
		return true;
	}
	// 标点拆分
	QRegularExpression puncReg("([，。；！？\\s])");
	QStringList tmpParts = text.split(puncReg, Qt::SkipEmptyParts);
	QString curPart;

	for (const QString& part : tmpParts) {
		if (curPart.length() + part.length() <= m_maxTextLength)	curPart += part;
		else {
			// 当前片段非空，先添加到结果
			if (!curPart.isEmpty()) {
				parts.append(curPart);
				curPart = part;
			} else {
				// 强制按长度拆分
				for (int i = 0; i < part.length(); i += m_maxTextLength) {
					parts.append(part.mid(i, m_maxTextLength));
				}
				curPart.clear();
			}
		}
	}
	if (!curPart.isEmpty())	parts.append(curPart);
	// check 所有长度
	for (const QString& part : parts) {
		if (part.length() > m_maxTextLength) {
            qWarning() << "[BaiduPinyinApi] 拆分后仍有片段超200字符：" << part;
            return false;
        }
	}
	qDebug() << "[BaiduPinyinApi] 长文本拆分：原长度" << text.length() << "→" << parts.size() << "段";
    return true;
}

QString BaiduPinyinApi::getPinyinFromCache(const QString &text)
{
	QMutexLocker locker(&m_instanceMutex);
	// memory cache check
	if (m_memoryCache.contains(text)) {
		qDebug() << "[BaiduPinyinApi] 内存缓存命中：" << text;
        return m_memoryCache[text];
	}
	// local cache check
	QJsonObject cacheObj = m_localCacheDoc.object()["cache"].toObject();
	if (cacheObj.contains(text)) {
        QString pinyin = cacheObj[text].toString();
        m_memoryCache[text] = pinyin;
        qDebug() << "[BaiduPinyinApi] 本地缓存命中：" << text;
        return pinyin;
    }
    return "";  // 缓存未命中
}

void BaiduPinyinApi::savePinyinToCache(const QString &text, const QString &pinyin)
{
	QMutexLocker locker(&m_instanceMutex);
    // 写入内存缓存
    m_memoryCache[text] = pinyin;
    // 写入本地缓存
    QJsonObject root = m_localCacheDoc.object();
    QJsonObject cacheObj = root["cache"].toObject();
    cacheObj[text] = pinyin;
    root["cache"] = cacheObj;
    m_localCacheDoc.setObject(root);
    // 异步保存到文件
    QTimer::singleShot(0, this, &BaiduPinyinApi::saveLocalCache);
}

void BaiduPinyinApi::loadLocalCache()
{
	QFile file(m_localCachePath);
	if (!file.open(QIODevice::ReadOnly)) {
		// 含polyphonic_words字段
		QJsonObject root;
		root["cache"] = QJsonObject();
        // 默认多音字库（离线兜底）
        root["polyphonic_words"] = QJsonObject({
            {"重庆", "chóng qìng"},
            {"重量", "zhòng liàng"},
            {"行", "xíng;háng"},
            {"好", "hǎo;hào"},
            {"乐", "lè;yuè"}
        });
		m_localCacheDoc.setObject(root);
        qWarning() << "[BaiduPinyinApi] 本地缓存不存在，创建空缓存：" << m_localCachePath;
        return;
	}

	QByteArray data = file.readAll();
	file.close();
	QJsonParseError parseErr;
	m_localCacheDoc = QJsonDocument::fromJson(data, &parseErr);
	if (parseErr.error != QJsonParseError::NoError) {
		 qWarning() << "[BaiduPinyinApi] 本地缓存解析失败：" << parseErr.errorString();
        // 初始化空缓存
        QJsonObject root;
        root["cache"] = QJsonObject();
        root["polyphonic_words"] = QJsonObject();
        m_localCacheDoc.setObject(root);
	} else		
	qInfo() << "[BaiduPinyinApi] 本地缓存加载成功，条目数：" << m_localCacheDoc.object()["cache"].toObject().size();
}

void BaiduPinyinApi::saveLocalCache()
{
	QFile file(m_localCachePath);
	if (!file.open(QIODevice::WriteOnly)) {
		qWarning() << "[BaiduPinyinApi] 保存本地缓存失败：" << file.errorString();
		return;
	}

    file.write(m_localCacheDoc.toJson(QJsonDocument::Indented));
    file.close();
}

QString BaiduPinyinApi::parseApiResponse(const QByteArray &response, ApiErrorCode &code)
{
	QJsonParseError parseErr;
    QJsonDocument doc = QJsonDocument::fromJson(response, &parseErr);
    if (parseErr.error != QJsonParseError::NoError) {
        code = ApiErrorCode::JsonParseError;
        return parseErr.errorString();
    }

    QJsonObject root = doc.object();
    // 检查错误码
    if (root.contains("error_code")) {
        int errorCode = root["error_code"].toInt();
        switch (errorCode) {
            case 10001: case 10002: case 10003: code = ApiErrorCode::AppIdError; break;
            case 10004: case 10009: code = ApiErrorCode::SecretKeyError; break;
            case 10010: code = ApiErrorCode::QuotaExceed; break;
            case 10015: code = ApiErrorCode::LengthExceed; break;
            default: code = ApiErrorCode::NetworkError;
        }
        return root["error_msg"].toString();
    }

    // 解析拼音结果
    QJsonArray transArr = root["trans_result"].toArray();
    if (transArr.isEmpty()) {
        code = ApiErrorCode::JsonParseError;
        return "未获取到拼音结果";
    }

    QString pinyin;
    for (const QJsonValue& val : transArr) {
        pinyin += val.toObject()["dst"].toString() + " ";
    }
    code = ApiErrorCode::Success;
    return pinyin.trimmed();
}
